package spindles.api.domain;

import static com.sleepycat.persist.model.Relationship.ONE_TO_ONE;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

import javax.measure.quantities.Duration;
import javax.measure.quantities.Quantity;
import javax.measure.quantities.Scalar;
import javax.measure.units.SI;

import org.apache.commons.lang.Validate;

import spindles.api.util.ErrorMessages;
import spindles.api.util.MathUtil;
import spindles.api.util.Util;

import com.sleepycat.persist.model.Entity;
import com.sleepycat.persist.model.SecondaryKey;

@Entity
public class ThresholdGroup extends DomainObject{	
		
	/**
	 * 
	 */
	private static transient final long serialVersionUID = -1315326880537812884L;

	/**
	 * Hard criterion groups.
	 */
	
	public enum ThresholdGroupName{
		// 0.3 sec
		GROUP_9_3, GROUP_8_3, GROUP_7_3, GROUP_6_3, GROUP_5_3,
		// 0.4 sec
		GROUP_9_4, GROUP_8_4, GROUP_7_4, GROUP_6_4, GROUP_5_4,
		// 0.5 sec
		GROUP_9_5, GROUP_8_5, GROUP_7_5, GROUP_6_5, GROUP_5_5
	}		
	
	/**
	 * The part of the SY curve that is higher than Vt is considered
	 * to be a peak.
	 */
	private BigDecimal Vt;
	
	/**
	 * Used in peak-merging process. The minimum SY amplitude allowed for
	 * peaks to merge.
	 */
	private BigDecimal Vg;
	
	/**
	 * The maximum time gap between peaks that can be merged.
	 */
	private BigDecimal Tg;
	
	/**
	 * Hard-criterion: Peaks lasting longer than minSpindleDuration.
	 */
	private BigDecimal minSpindleDuration;
	
	@SecondaryKey(relate=ONE_TO_ONE)
	private String groupName;
	
	private String comments;
	
	private boolean defaultGroup;	
	
	private ThresholdGroup(){};
	
	public ThresholdGroup(double Vt, boolean isDefault, 
			String groupName, String comments){
		this(Vt, Vt - 0.1, 0.3, 0.0, isDefault, groupName, comments);
	}
	
	public ThresholdGroup(double Vt, String groupName, String comments)	{
		this(Vt, Vt - 0.1, 0.3, 0.0, false, groupName, comments);
	}
	
	public ThresholdGroup(double Vt, double minSpindleDuration,
			boolean isDefault,
			String groupName) {
		this(Vt, Vt - 0.1, 0.3, minSpindleDuration, isDefault, groupName, "");
	}
	
	public ThresholdGroup(double Vt, double minSpindleDuration,
			String groupName) {
		this(Vt, Vt - 0.1, 0.3, minSpindleDuration, false, groupName, "");
	}
	
	public ThresholdGroup(double Vt, double minSpindleDuration,
			boolean isDefault,
			String groupName, String comments) {
		this(Vt, Vt - 0.1, 0.3, minSpindleDuration, isDefault, groupName, comments);
	}
	
	public ThresholdGroup(double Vt, double minSpindleDuration,
			String groupName, String comments) {
		this(Vt, Vt - 0.1, 0.3, minSpindleDuration, false, groupName, comments);
	}
	
	public ThresholdGroup(double Vt, double Vg, 
			double Tg, double minSpindleDuration,
			boolean isDefault,
			String groupName, String comments){
		setVt(BigDecimal.valueOf(Vt));
		setVg(BigDecimal.valueOf(Vg));
		setTg(new Scalar<Duration>(Tg, SI.SECOND));
		setMinSpindleDuration(new Scalar<Duration>(minSpindleDuration, SI.SECOND));
		setGroupName(groupName);
		setComments(comments);
		setDefaultGroup(isDefault);
	}
	
	public static List<ThresholdGroup> findAll(){
		List<ThresholdGroup> all = new ArrayList<ThresholdGroup>();
		
		all.add(new ThresholdGroup(0.9, 0.3, true, "GROUP_9_3"));
		all.add(new ThresholdGroup(0.8, 0.3, "GROUP_8_3"));
		all.add(new ThresholdGroup(0.7, 0.3, "GROUP_7_3"));		
		all.add(new ThresholdGroup(0.6, 0.3, "GROUP_6_3"));
		all.add(new ThresholdGroup(0.5, 0.3, "GROUP_5_3"));
		
		all.add(new ThresholdGroup(0.9, 0.4, "GROUP_9_4"));		
		all.add(new ThresholdGroup(0.8, 0.4, "GROUP_8_4"));
		all.add(new ThresholdGroup(0.7, 0.4, "GROUP_7_4"));
		all.add(new ThresholdGroup(0.6, 0.4, "GROUP_6_4"));		
		all.add(new ThresholdGroup(0.5, 0.4, "GROUP_5_4"));
		
		all.add(new ThresholdGroup(0.9, 0.5, "GROUP_9_5"));
		all.add(new ThresholdGroup(0.8, 0.5, "GROUP_8_5"));		
		all.add(new ThresholdGroup(0.7, 0.5, "GROUP_7_5"));
		all.add(new ThresholdGroup(0.6, 0.5, "GROUP_6_5"));
		all.add(new ThresholdGroup(0.5, 0.5, "GROUP_5_5"));
		
		return all;
	}

	public BigDecimal getVt() {
		return Vt;
	}
	
	public double getVtDouble(){
		return Vt.doubleValue();
	}

	public void setVt(BigDecimal vt) {
		validate(vt);
		Vt = vt;
	}
	
	private void validate(BigDecimal value){
		Validate.isTrue(MathUtil.isBetween(value, 0, 1), 
				String.format(ErrorMessages.OUT_OF_LIMITS, 0, 1));		
	}

	public BigDecimal getVg() {
		return Vg;
	}
	
	public double getVgDouble(){
		return Vg.doubleValue();
	}

	public void setVg(BigDecimal vg) {
		validate(vg);
		Vg = vg;
	}

	public Quantity<Duration> getMinSpindleDuration() {
		return new Scalar<Duration>(minSpindleDuration.doubleValue(), SI.SECOND);
	}
	
	public BigDecimal getScalarMinSpindleDuration(){
		return minSpindleDuration;
	}

	public void setMinSpindleDuration(Quantity<Duration> minSpindleDuration) {
		this.minSpindleDuration = 
			BigDecimal.valueOf(minSpindleDuration.doubleValue(SI.SECOND));
	}

	public Quantity<Duration> getTg() {
		return new Scalar<Duration>(Tg.doubleValue(), SI.SECOND);		
	}

	public void setTg(Quantity<Duration> tg) {
		Tg = BigDecimal.valueOf(tg.doubleValue(SI.SECOND));
	}

	public String getComments() {
		return comments;
	}
	
	public String getHardCriterionDescription(){
		return "Detection Threshold: " + getVt() + " - Minimum Spindle Duration: " 
		+ getMinSpindleDuration().doubleValue(SI.SECOND) + " sec";
	}
	
	public String getSoftCriterionDescription(){
		return "Detection Threshold: " + getVt();
	}

	public void setComments(String comments) {
		this.comments = comments;
	}

	public String getGroupName() {
		return groupName;
	}

	public void setGroupName(String groupName) {
		Validate.isTrue(!Util.isEmpty(groupName), "Threshold group name cannot be empty");		
		this.groupName = groupName;
	}

	public boolean isDefaultGroup() {
		return defaultGroup;
	}

	public void setDefaultGroup(boolean defaultGroup) {
		this.defaultGroup = defaultGroup;
	}
	
	public String toString(){
		return "Detection Threshold: " + Vt + ", Min. Spindle Duration: " + minSpindleDuration + "sec";
	}
	
	
}
